package org.hawk.gwt.ppc.scanner;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

import com.sun.org.apache.xalan.internal.xsltc.compiler.SourceLoader;

/**
 * Composite sources loader that delegates all operations to 
 * correct loaders for each path element.
 * @author alex.bereznevatiy@gmail.com
 */
public class UniversalSourcesLoader implements SourcesLoader {
	private List<SourcesLoader> loaders;
	/**
	 * Creates loader for the classpath elements passed.
	 * @param classPath
	 * @throws IOException in case of unable to open jar
	 * @throws IllegalArgumentException in case of 
	 * not supported path element
	 */
	public UniversalSourcesLoader(String[] classPath) throws IOException{
		loaders = new LinkedList<SourcesLoader>();
		resolve(classPath);
	}
	/**
	 * Creates loader for the default classpath of current 
	 * VM run. All classes and sources will be loaded from
	 * current classloaders set.
	 * @throws IOException
	 */
	public UniversalSourcesLoader() throws IOException {
		this(resolveClassPath());
	}

	private static String[] resolveClassPath() {
		Properties env = System.getProperties();
		String prop = env.getProperty("java.class.path", "").trim();
		if(prop.length()==0){
			return new String[0];
		}
		return prop.split(File.pathSeparator);
	}

	private void resolve(String[] classPath) throws IOException {
		StringBuilder unsupported = new StringBuilder();
		for(String element : classPath){
			resolve(unsupported, element);
		}
		if(unsupported.length()>0){
			unsupported.append(']');
			throw new IllegalArgumentException(
				"Unsupported path element(s): "+unsupported);
		}
	}

	private void resolve(StringBuilder unsupported, String element) throws IOException {
		File file = new File(element);
		if(file.isDirectory()){
			loaders.add(new FileSystemSourcesLoader(file));
			return;
		}
		else if(file.isFile()&&element.endsWith(".jar")){
			loaders.add(new JarSourcesLoader(file));
			return;
		}
		if(unsupported.length()>0){
			unsupported.append("],");
		}
		unsupported.append(element);
	}
	/**
	 * @return loaders used by this universal loader to
	 * resolve sources within classpath.
	 */
	public List<SourcesLoader> getClassPath() {
		return Collections.unmodifiableList(loaders);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.SourcesLoader#getClasses(java.lang.String)
	 */
	public List<String> getClasses(String packageName) {
		ArrayList<String> result = new ArrayList<String>();
		for(SourcesLoader loader : loaders){
			List<String> classes = new ArrayList<String>(
					loader.getClasses(packageName));
			classes.removeAll(result);
			result.addAll(classes);
		}
		result.trimToSize();
		return Collections.unmodifiableList(result);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.SourcesLoader#getSubpackages(java.lang.String)
	 */
	public List<String> getSubpackages(String packageName) {
		ArrayList<String> result = new ArrayList<String>();
		for(SourcesLoader loader : loaders){
			List<String> packages = new ArrayList<String>(
					loader.getSubpackages(packageName));
			packages.removeAll(result);
			result.addAll(packages);
		}
		result.trimToSize();
		return Collections.unmodifiableList(result);
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.SourcesLoader#getResource(java.lang.String)
	 */
	public URL getResource(String name) {
		for(SourcesLoader loader : loaders){
			URL rez = loader.getResource(name);
			if(rez!=null)return rez;
		}
		return null;
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.SourcesLoader#getClass(java.lang.String)
	 */
	public URL getClass(String name) {
		for(SourcesLoader loader : loaders){
			URL rez = loader.getClass(name);
			if(rez!=null)return rez;
		}
		return null;
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.SourcesLoader#isClassExists(java.lang.String)
	 */
	public boolean isClassExists(String className) {
		for(SourcesLoader loader : loaders){
			if(loader.isClassExists(className)){
				return true;
			}
		}
		return false;
	}
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "UniversalSourcesLoader[" + loaders + "]";
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.SourcesLoader#toUrl()
	 */
	public URL toUrl() {
		return null;
	}
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if(this==obj)return true;
		if(obj instanceof UniversalSourcesLoader){
			UniversalSourcesLoader other = (UniversalSourcesLoader)obj;
			return other.loaders.size()==loaders.size()&&
				other.loaders.containsAll(loaders);
		}
		else if (obj instanceof SourceLoader){
			return loaders.size()==1&&
				loaders.get(0).equals(obj);
		}
		return false;
	}
}
